package com.yxs.go_bang.api;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.yxs.go_bang.game.*;
import com.yxs.go_bang.mapper.UserMapper;
import com.yxs.go_bang.model.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;

import java.io.IOException;

@Component
public class GameAPI extends TextWebSocketHandler {
    @Autowired
    private ObjectMapper objectMapper;
    @Autowired
    private RoomManager roomManager;
    @Autowired
    private OnlineUserManaage onlineUserManaage;

    @Autowired
    private UserMapper userMapper;

    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
        GameReadyResponse response=new GameReadyResponse();
        System.out.println("GameApi连接成功");
        User user=(User) session.getAttributes().get("user");
        if (user==null){
            response.setOk(false);
            response.setReason("用户未登录");
            session.sendMessage(new TextMessage(objectMapper.writeValueAsString(response)));
            return;
        }
        // 2.判断当前用户是否已经进入房间
        Room room=roomManager.getRoomByUserId(user.getUserId());
        if (room==null){
            response.setOk(false);
            response.setReason("玩家匹配失败！");
            session.sendMessage(new TextMessage(objectMapper.writeValueAsString(response)));
            return;
        }
        // 3. 判定当前是否为多开
        if (onlineUserManaage.getFromGameHall(user.getUserId())!=null ||
                onlineUserManaage.getFromGameRoom(user.getUserId())!=null){
            response.setOk(true);
            response.setMessage("repeatConnection");
            response.setReason("禁止用户多开！");
            session.sendMessage(new TextMessage(objectMapper.writeValueAsString(response)));
            return;
        }

        // 4.设置玩家上线
        onlineUserManaage.enterGameRoom(user.getUserId(), session);
        // 5.把两个玩家加入到游戏房间中
        // 在页面完成从 hall.html -> room.html 后才进行这个操作
        // (页面跳转是有可能失败的)

        // 5.把玩家加入到游戏房间
        // 是一个多个客服端进行并发的操作，需要解决线程安全问题
        // 多个线程竞争针对room对象进行修改操作，竞争的是room对象
        // 所以就针对room对象进行加锁，保证了线程安全
        synchronized (room) {
            if (room.getUser1() == null) {
                // 就把当前连着上来的玩家作为user1加入
                room.setUser1(user);
                System.out.println("玩家 " + user.getUsername() + "已经准备就绪,作为玩家1");
                // 先连入房间的玩家作为先手
                room.setWhiteUser(user.getUserId());
                return;
            }
            if (room.getUser2() == null) {
                // 就把当前连着上来的玩家作为user1加入
                room.setUser2(user);
                System.out.println("玩家 " + user.getUsername() + "已经准备就绪，作为玩家2");

                // 当两个玩家都加入游戏房间之后，就给双方都返回一个websocket响应
                // 通知两个玩家，游戏已经准备好了，可以发车
                // 通知玩家1
                noticeGameReady(room, room.getUser1(), room.getUser2());
                // 通知玩家2
                noticeGameReady(room, room.getUser2(), room.getUser1());
                return;
            }
        }

        // 如果有第三个玩家尝试加入房间，提示报错
        // 理论不存在，但是增加健壮性
        response.setOk(false);
        response.setReason("房间已满！");
        session.sendMessage(new TextMessage(objectMapper.writeValueAsString(response)));

    }

    private void noticeGameReady(Room room, User thisUser, User thatUser) throws IOException {
        GameReadyResponse response=new GameReadyResponse();
        response.setMessage("gameReady");
        response.setOk(true);
        response.setReason("");
        response.setRoomId(room.getRoomId());
        response.setThisUserId(thisUser.getUserId());
        response.setThatUserId(thatUser.getUserId());
        response.setWhiteUser(room.getWhiteUser());
        // 响应数据给玩家客服端
        WebSocketSession webSocketSession=onlineUserManaage.getFromGameRoom(thisUser.getUserId());
        webSocketSession.sendMessage(new TextMessage(objectMapper.writeValueAsString(response)));

    }

    @Override
    protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
       System.out.println("game api 收到消息："+message.getPayload());
//        session.sendMessage(message);
        User user = (User) session.getAttributes().get("user");
        if (user==null){
            return;
        }
        // 根据玩家id获取房间对象
        Room room=roomManager.getRoomByUserId(user.getUserId());
        // 通过room对象来处理落子请求
        room.putChess(message.getPayload());
    }

    @Override
    public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
        System.out.println("game api 连接异常");
        User user=(User) session.getAttributes().get("user");
        if (user==null){
            // 就不返回响应给客服端了
            return;
        }
        WebSocketSession exitSession=onlineUserManaage.getFromGameRoom(user.getUserId());
        if (session == exitSession){
            // 避免多开情况下用户2退出导致用户1的信息被删除
            onlineUserManaage.exitGameRoom(user.getUserId());
        }
        System.out.println("用户 :" +user.getUsername()+"游戏房间房间连接异常");
        // 自己掉线，通知对方获胜
        noticeThatUserWin(user);
    }

    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
        System.out.println("game api 连接关闭");
        User user=(User) session.getAttributes().get("user");
        if (user==null){
            // 就不返回响应给客服端了
            return;
        }
        WebSocketSession exitSession=onlineUserManaage.getFromGameRoom(user.getUserId());
        if (session == exitSession){
            // 避免多开情况下用户2退出导致用户1的信息被删除
            onlineUserManaage.exitGameRoom(user.getUserId());
        }
        System.out.println("用户 :" +user.getUsername()+"离开游戏房间");

        noticeThatUserWin(user);

    }

    private void noticeThatUserWin(User thisUser) throws IOException {
        Room room=roomManager.getRoomByUserId(thisUser.getUserId());
        if (room==null){
            System.out.println("房间已被销毁，无法通知对手");
            return;
        }
        // 根据房间找到对手
        User thatUser=(thisUser==room.getUser1())?room.getUser2():room.getUser1();
        // 得到对手在线状态
        WebSocketSession webSocketSession=onlineUserManaage.getFromGameRoom(thatUser.getUserId());
        if (webSocketSession==null){
            // 对手也掉线
            System.out.println("对手也同样掉线，无需通知");
            return;
        }
        // 构造响应，通知对手获胜
        GameResponse response=new GameResponse();
        response.setMessage("putChess");
        response.setUserId(thatUser.getUserId());
        response.setWinner(thatUser.getUserId());
        response.setWinner(-1);
        webSocketSession.sendMessage(new TextMessage(objectMapper.writeValueAsString(response)));

        // 更新用户信息
        int winUserId= thatUser.getUserId();
        int loseUserId=thisUser.getUserId();
        userMapper.userWin(winUserId);
        userMapper.loseWin(loseUserId);


        // 游戏结束，销毁游戏房间
        roomManager.remove(room.getRoomId(), room.getUser1().getUserId(),room.getUser2().getUserId());
        System.out.println("有玩家掉线，游戏结束，游戏房间："+room.getRoomId()+" 已销毁");


    }
}
